package org.jetbrains.plugins.scala.lang.parser.parsing.top

import com.intellij.psi.tree.IElementType
import org.jetbrains.plugins.scala.ScalaBundle
import org.jetbrains.plugins.scala.lang.lexer.{ScalaTokenType, ScalaTokenTypes}
import org.jetbrains.plugins.scala.lang.lexer.ScalaTokenTypes.{kWITH, tCOMMA}
import org.jetbrains.plugins.scala.lang.parser.ScalaElementType
import org.jetbrains.plugins.scala.lang.parser.parsing.ParsingRule
import org.jetbrains.plugins.scala.lang.parser.parsing.base.Constructor
import org.jetbrains.plugins.scala.lang.parser.parsing.builder.ScalaPsiBuilder

import scala.annotation.tailrec

sealed abstract class Parents(
  val allowCommaSeparatedParentsInScala3: Boolean = true,
  val allowEmptyParents:                  Boolean = false,
  val allowRefinement:                    Boolean = true,
  val allowNewlineBeforeParent:           Boolean = true,
) extends ParsingRule {
  override def parse(implicit builder: ScalaPsiBuilder): true = {
    val marker = builder.mark()

    if (parseParent()) {
      parseNextSimpleTypes()
    }

    marker.done(ScalaElementType.TEMPLATE_PARENTS)
    true
  }

  @tailrec
  private def parseNextSimpleTypes()(implicit builder: ScalaPsiBuilder): Unit =
    builder.getTokenType match {
      case `kWITH` if !allowRefinement && ScalaTokenTypes.LBRACE_OR_COLON_TOKEN_SET.contains(builder.lookAhead(1)) =>
        // for old given syntax
      case `kWITH` | CommaIfAllowed() =>
        val withRollback = builder.mark()
        val wasWith = builder.getTokenType == kWITH
        builder.advanceLexer() // Ate with
        if (wasWith && !allowNewlineBeforeParent && builder.newlineBeforeCurrentToken) {
          // for given parents
          withRollback.rollbackTo()
        } else {
          withRollback.drop()
          if ( parseParent()) {
            parseNextSimpleTypes()
          }
        }
      case _ =>
    }

  private def parseParent()(implicit builder: ScalaPsiBuilder): Boolean = {
    val result = Constructor()

    if (!result && !allowEmptyParents) {
      builder.error(ScalaBundle.message("identifier.expected"))
    }

    result
  }

  private object CommaIfAllowed {
    def unapply(element: IElementType)(implicit builder: ScalaPsiBuilder): Boolean =
      element == tCOMMA && builder.isScala3 && allowCommaSeparatedParentsInScala3
  }
}

object NewTemplateDefParents extends Parents(allowCommaSeparatedParentsInScala3 = false, allowEmptyParents = true)
object TypeDefinitionParents extends Parents
object GivenParents extends Parents(allowRefinement = false, allowNewlineBeforeParent = false)